﻿using System.Collections;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Net;
using System;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// 游戏更新和子App的下载
/// </summary>
public class AppUpdateAndDownloader:MonoBehaviour {

    private static AppUpdateAndDownloader m_instance;
    public static AppUpdateAndDownloader Instance {
        get {
            if (m_instance == null) {
                GameObject go = new GameObject ();
                m_instance = go.AddComponent<AppUpdateAndDownloader> ();
            }
            return m_instance;
        }
    }

    public static bool IsInited(){
        return m_instance != null;
    }

    private const string APP_INSTALLED_KEY = "MainApp_Installed_Apps";//已经安装的appkey

    private const string WEBURL = "https://firebasestorage.googleapis.com/v0/b/test-98aa6.appspot.com/o/";

    public Action<float> OnProgress = null;//初始时释放资源进度，更新游戏进度，下载一个子App进度
    public Action<string> OnComplete = null; //初始时释放资源完成，更新游戏完成，下载一个子App完成
	public Action<string> OnFail = null;//下载失败

    private Queue<string> _sonAppQueue ;

    private string _currentSonApp = null;
    private bool _isDownloading = false;

    private string[] _files;//所有的

    private HttpDownLoad _httpDownload;

    private List<string> _currentAppFiles;//当前子app的

    private object o = new object();

    void Awake(){
        if (m_instance != null) {
            Destroy (gameObject);
            return;
        }
        m_instance = this;
        name = "[AppUpdateAndDownloader]";

        DontDestroyOnLoad(gameObject);

        _sonAppQueue = new Queue<string>();
        _httpDownload = new HttpDownLoad();
        _currentAppFiles = new List<string>();

        if(!PlayerPrefs.HasKey(APP_INSTALLED_KEY)){
			PlayerPrefsX.SetStringArray(APP_INSTALLED_KEY,new string[]{"lua","mainapp"});
            PlayerPrefs.Save();
        }
    }

    void Start(){

        DownloadFile("Plist2Images.app.zip", "Plist2Images.app.zip", delegate(float obj)
            {
                UnityEngine.Debug.LogWarning("下载进度:"+obj);
            }, delegate()
            {
                UnityEngine.Debug.LogWarning("下载成功");
            }, delegate()
            {
                UnityEngine.Debug.LogWarning("下载失败");
            });
    }

    /// <summary>
    /// 初始化游戏
    /// </summary>
    public void InitGame(){
        //复制资源到persistentDataPath目录
        StartCoroutine(OnExtractResource());
    }

    IEnumerator OnExtractResource() {
        if (File.Exists(Application.persistentDataPath + "/files.txt"))
        {
            if (OnComplete != null)
                OnComplete("MainApp");
            yield break;
        }
        WWW www = new WWW(ResManager.streamingAssetPath + "files.txt");
        yield return www;
        if (www.error != null)
        {
            yield break;
        }
        //释放所有文件到数据目录
        string[] files = www.text.Split('\n');
        for(int i  =0 ;i<files.Length;++i) {
            string file = files[i];
            if (file.Length < 2)
                continue;
            string[] fs = file.Split('|');

            if (File.Exists(Application.persistentDataPath + "/" + fs[0]))
            {
                File.Delete(Application.persistentDataPath + "/" + fs[0]);
            }

            if (Application.platform == RuntimePlatform.Android) {
                yield return StartCoroutine(CopyFile(ResManager.streamingAssetPath + fs[0],Application.persistentDataPath + "/" + fs[0]));
            } else {
                yield return StartCoroutine(CopyFile(Application.streamingAssetsPath + "/" + fs[0],Application.persistentDataPath + "/" + fs[0]));
            }
            if (OnProgress != null)
                OnProgress(1f / file.Length);
        }
        //保存files.txt
        File.WriteAllText(Application.persistentDataPath + "/files.txt",www.text);
        www.Dispose();
        yield return new WaitForSeconds(0.1f);
        if (OnComplete != null)
            OnComplete("MainApp");
    }
    IEnumerator CopyFile(string srcFile,string desFile){

        string dir = Path.GetDirectoryName(desFile);
        if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

        if (Application.platform == RuntimePlatform.Android) {
            WWW www = new WWW(srcFile);
            yield return www;

            if (www.isDone) {
                File.WriteAllBytes(desFile, www.bytes);
            }
            yield return 0;
        } else {
            File.Copy(srcFile, desFile, true);
        }
        yield return new WaitForEndOfFrame();
    }

    /// <summary>
    /// 更新游戏
    /// </summary>
    public void UpdateGame(){
        StartCoroutine(OnUpdateResource());
    }

    IEnumerator OnUpdateResource(){
        string listUrl = LuaFramework.AppConst.WebUrl + LuaFramework.AppConst.mainAppVersion + "/files.txt?v=" + DateTime.Now.ToString("yyyymmddhhmmss");

        WWW www = new WWW(listUrl); yield return www;
        if (www.error != null) { //如果失败就直接完成
			UnityEngine.Debug.LogError("AppUpdateAndDownload>> 更新失败");
            if(OnComplete!=null) OnComplete("MainApp");
            yield break;
        }
        if (!Directory.Exists(Application.persistentDataPath)){
            Directory.CreateDirectory(Application.persistentDataPath);
        }
        File.WriteAllBytes(Application.persistentDataPath + "/files.txt", www.bytes);

        string filesText = www.text;
        _files = filesText.Split('\n');
        www.Dispose();

        string[] installed = PlayerPrefsX.GetStringArray(APP_INSTALLED_KEY);
        //收集
        _currentAppFiles.Clear();
        for (int i = 0; i < _files.Length; ++i)
        {
			string file = _files[i];
            if (file.Length < 2)
                continue;
            for (int j = 0; j < installed.Length; ++j)
            {
				if (file.ToLower().IndexOf(installed[j]+"/".ToLower()) == 0 || file.ToLower().IndexOf(installed[j]+"raw/".ToLower()) == 0)
                {
                    _currentAppFiles.Add(file);
                }
            }
        }

        _currentSonApp = "MainApp";
        if (_files != null && _currentAppFiles.Count > 0)
        {
            _isDownloading = true;
            string random = DateTime.Now.ToString("yyyymmddhhmmss");
            int len = _currentAppFiles.Count;
            string dataPath = Application.persistentDataPath + "/";
            for (int i = 0; i < len; ++i)
            {
                string[] keyValue = _currentAppFiles[i].Split('|');
                string f = keyValue[0];
                string localfile = (dataPath + f).Trim();
                string path = Path.GetDirectoryName(localfile);
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                string fileUrl = LuaFramework.AppConst.WebUrl + LuaFramework.AppConst.mainAppVersion + "/" + f + "?v=" + random;
                bool canUpdate = !File.Exists(localfile);
                if (!canUpdate)
                {
                    string remoteMd5 = keyValue[1].Trim();
                    string localMd5 = LuaFramework.Util.md5file(localfile);
                    canUpdate = !remoteMd5.Equals(localMd5);
                    if (canUpdate)
                        File.Delete(localfile);
                }
                if (canUpdate)
                {   //本地缺少文件
                    //这里都是资源文件，用线程下载
                    _httpDownload.DownLoad(fileUrl, localfile, null);
                    while (!_httpDownload.isDone)
                    {
                        yield return new WaitForEndOfFrame();
                    }
					if(_httpDownload.isFail){
						if (OnFail != null)
							OnFail(_currentSonApp);
						yield break;
					}
                }
                if (OnProgress != null)
                    OnProgress((float)i / len);
            }
        }
        yield return new WaitForEndOfFrame();

        if (OnComplete != null)
            OnComplete(_currentSonApp);
        _isDownloading = false;
    }


    /// <summary>
    /// 下载子App
    /// </summary>
    /// <param name="sonAppName">Son app name.</param>
    public void DownloadSonApp(string sonAppName)
    {
        _sonAppQueue.Enqueue(sonAppName);
        StartCoroutine(DownloadNextApp());
    }

    //加载下一个App
    IEnumerator DownloadNextApp(){
		if (_files !=null && !_isDownloading && _sonAppQueue.Count > 0)
        {
            _currentSonApp = _sonAppQueue.Dequeue();

            //读取file
            ReadLocalFile();
            CollectSonAppFiles();

            if (_currentAppFiles.Count > 0)
            {
                string random = DateTime.Now.ToString("yyyymmddhhmmss");
                int len = _currentAppFiles.Count;
                string dataPath = Application.persistentDataPath + "/";
                for (int i = 0; i < len; ++i)
                {
                    string[] keyValue = _currentAppFiles[i].Split('|');
                    string f = keyValue[0];
                    string localfile = (dataPath + f).Trim();
                    string path = Path.GetDirectoryName(localfile);
                    if (!Directory.Exists(path))
                    {
                        Directory.CreateDirectory(path);
                    }

                    string fileUrl = LuaFramework.AppConst.WebUrl + LuaFramework.AppConst.mainAppVersion + "/" + f + "?v=" + random;
                    bool canUpdate = !File.Exists(localfile);
                    if (!canUpdate)
                    {
                        string remoteMd5 = keyValue[1].Trim();
                        string localMd5 = LuaFramework.Util.md5file(localfile);
                        canUpdate = !remoteMd5.Equals(localMd5);
                        if (canUpdate)
                            File.Delete(localfile);
                    }
                    if (canUpdate)
                    {   //本地缺少文件
                        //这里都是资源文件，用线程下载
						_httpDownload.DownLoad(fileUrl, localfile, null ,System.Threading.ThreadPriority.BelowNormal);
                        while (!_httpDownload.isDone)
                        {
                            yield return new WaitForEndOfFrame();
                        }
                    }
                    if (OnProgress != null)
                        OnProgress((float)i / len);
                }
            }
            yield return new WaitForEndOfFrame();


            string[] temp = PlayerPrefsX.GetStringArray(APP_INSTALLED_KEY);
            List<string> apps = new List<string>(temp);
            apps.Add(_currentSonApp);
            PlayerPrefsX.SetStringArray(APP_INSTALLED_KEY,apps.ToArray());
            PlayerPrefs.Save();

            if (OnComplete != null)
                OnComplete(_currentSonApp);

            //继续下一个子App
            if (_sonAppQueue.Count > 0)
            {
                StartCoroutine(DownloadNextApp());
            }
        }
    }

    void ReadLocalFile(){
        if (_files == null)
        {
            string filePath = Application.persistentDataPath + "/files.txt";
            if (File.Exists(filePath))
            {
                _files = File.ReadAllLines(filePath, System.Text.Encoding.UTF8);
            }
        }
    }

    /// <summary>
    /// 收集当前子App需要下载的资源
    /// </summary>
    void CollectSonAppFiles(){
        if (!string.IsNullOrEmpty(_currentSonApp))
        {
            _currentAppFiles.Clear();
            int len = _files.Length;
			string lower = _currentSonApp.ToLower();
            for (int i = 0; i < len; ++i)
            {
                string line = _files[i];
				if (line.ToLower().IndexOf(lower) == 0)
                {
                    _currentAppFiles.Add(line);
                }
            }
        }
    }


    public void CancelDownload(){
        if (_request != null)
        {
            _request.Abort();
            _request.Dispose();
        }
        if (_fs != null)
        {
            _fs.Close();
            _fs.Dispose();
            _fs = null;
        }
        StopAllCoroutines();
        _isLoading = false;
    }








    #region 下载文件，通过Firebase ,支持断点续传


    /// <summary>
    /// 下载文件到本地
    /// </summary>
    /// <param name="fileUrl">File URL.</param>
    /// <param name="localUrl">Local URL.</param>
    /// <param name="onProgress">On progress.</param>
    /// <param name="onComplete">On complete.</param>
    /// <param name="onFail">On fail.</param>
    public void DownloadFile(string fileUrl,string localUrl,Action<float> onProgress,Action onComplete,Action onFail){
        StartCoroutine(_DownLoad(fileUrl,localUrl,onProgress,onComplete,onFail));
    }






    private UnityWebRequest _request;
    private float _progress = 0f;
    private bool _isLoading = false;
    private bool _isFail = false;
    private FileStream _fs;
    private const int TIMEOUT = 60;//60秒

    void Update(){
        if (_isLoading && _request != null)
        {
            _progress = _request.downloadProgress;
        }
        else
        {
            _progress = 0f;
        }
    }

    [Serializable]
    class FileInfo{
        public string name = "";
        public string bucket = "";
        public string generation = "";
        public int metageneration = 0;
        public string contentType = "";
        public string timeCreated = "";
        public string updated = "";
        public string storageClass = "";
        public int size = 0;
        public string md5Hash = "";
        public string contentEncoding = "";
        public string contentDisposition = "";
        public string crc32c = "";
        public string etag = "";
        public string downloadTokens = "";
    }

    IEnumerator _DownLoad(string webRoot, string fileUrl ,string localUrl){
        if (_request != null)
            _request.Dispose();

        _progress = 0f;
        _isLoading = true;
        _isFail = false;

        fileUrl = fileUrl.Replace("/","%2F");
        string url = webRoot + fileUrl;
        UnityEngine.Debug.Log("Download..."+url);

        UnityWebRequest request = UnityWebRequest.Get(url);
        request.disposeDownloadHandlerOnDispose = true;
        request.timeout = TIMEOUT;
        yield return request.Send();
        if (request.isDone == false || request.isError)
        {
            request.Dispose();
            _isLoading = false;
            _isFail = true;
            yield break;
        }

        FileInfo info = null;
        try{
            info = JsonUtility.FromJson<FileInfo>(request.downloadHandler.text);
            if (info == null)
            {
                request.Dispose();
                _isLoading = false;
                yield break;
            }
            else
            {
                request.Dispose();
            }
        }catch(Exception e){
            UnityEngine.Debug.LogError(e.ToString());
            request.Dispose();
            _isLoading = false;
            _isFail = true;
            yield break;
        }

       
        localUrl = Application.persistentDataPath + "/" + localUrl;
        string folder = Path.GetDirectoryName(localUrl);
        if (!Directory.Exists(localUrl))
            Directory.CreateDirectory(folder);


        _fs = new FileStream(localUrl, FileMode.OpenOrCreate, FileAccess.Write);

        url = webRoot + fileUrl+"?alt=media&token="+info.downloadTokens;
        _request = UnityWebRequest.Get(url);
        _request.timeout = TIMEOUT;
        _request.SetRequestHeader("Range", "bytes = " + _fs.Length + "-");
        _request.downloadHandler = new CustomDownLoadHandler(_fs,new byte[1024],info.size);
        _request.disposeDownloadHandlerOnDispose = true;

        yield return _request.Send();
        _fs.Close();
        _fs.Dispose();
        _fs = null;

        if (_request.isDone == false || _request.isError)
        {
            _request.Dispose();
            _isLoading = false;
            _isFail = true;
            yield break;
        }
        _isLoading = false;

        _request.Dispose();
    }

    IEnumerator _DownLoad(string fileUrl,string localUrl,Action<float> onProgress,Action onComplete,Action onFail){
        StartCoroutine(_DownLoad(WEBURL, fileUrl,localUrl));
        while (_progress<1f )
        {
            if (_isFail)
            {
                break;
            }
            else
            {
                if (onProgress != null)
                    onProgress(_progress);
            }
            yield return null;
        }
        if (_isFail == false)
        {
            if (onComplete != null)
                onComplete();
        }
        else
        {
            if (onFail != null)
                onFail();
        }
    }


    class CustomDownLoadHandler:DownloadHandlerScript{

        private FileStream _fs;
        private long _length;
        private long _total;

        protected override float GetProgress()
        {
            if (_total == 0)
                return 0;
            if (_length > _total)
                return 0;
            return (float)_length/_total;
        }

        public CustomDownLoadHandler() : base() {}
        public CustomDownLoadHandler(byte[] buffer) : base(buffer){}
        public CustomDownLoadHandler(FileStream fs,byte[] buffer,long totalSize) : base(buffer){
            _total = totalSize;
            _fs = fs;
            _length = _fs.Length;
            _fs.Seek(_length, SeekOrigin.Begin);
        }

        protected override bool ReceiveData(byte[] bytes, int dataLength)
        {
            if (bytes == null || bytes.Length < 1)
            {
                return false;
            }
            _fs.Write(bytes, 0, dataLength);
            _length += dataLength;

            return true;
        }

    }
    #endregion
}
